/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.woundedwolf.android.tumbleweed.renderables;

import android.util.Log;
import com.woundedwolf.android.tumbleweed.Pixel;
import com.woundedwolf.android.tumbleweed.elements.Renderable;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import javax.microedition.khronos.opengles.GL10;

/**
 *
 * @author FlameDuck
 */
public class Pixels implements Renderable
{

	private final Object pixelLock;
	private Map<Pixel, Integer> pixelMap;
	private Queue<Pixel> deadPixels;
	private Queue<Pixel> bornPixels;
	private IntBuffer vertexBuffer;
	private ByteBuffer colorBuffer;
	private int pixelCount;
	private int livePixels;

	public Pixels(int maxPixels)
	{
		pixelCount = maxPixels;
		livePixels = 0;
		pixelLock = new Object();
		pixelMap = new ConcurrentHashMap<Pixel, Integer>();
		deadPixels = new ConcurrentLinkedQueue<Pixel>();
		bornPixels = new ConcurrentLinkedQueue<Pixel>();
		ByteBuffer vbb = ByteBuffer.allocateDirect(pixelCount * 4 * 2);
		vbb.order(ByteOrder.nativeOrder());
		vertexBuffer = vbb.asIntBuffer();
		colorBuffer = ByteBuffer.allocateDirect(pixelCount * 4);
	}

	public void draw(GL10 glContext)
	{

		while (!bornPixels.isEmpty())
		{
			Pixel newPixel = bornPixels.poll();
			Pixel oldPixel = deadPixels.poll();
			if (oldPixel != null)
				swapPixels(newPixel, oldPixel);
			else if (livePixels < pixelCount)
					insertPixel(newPixel);
			else
				break;
		}

		synchronized(pixelLock)
		{

			glContext.glEnableClientState(GL10.GL_VERTEX_ARRAY);
			glContext.glEnableClientState(GL10.GL_COLOR_ARRAY);

			glContext.glVertexPointer(2, GL10.GL_FLOAT, 0, vertexBuffer);
			glContext.glColorPointer(4, GL10.GL_UNSIGNED_BYTE, 0, colorBuffer);

			glContext.glDrawArrays(GL10.GL_POINTS, 0, livePixels);

			glContext.glDisableClientState(GL10.GL_COLOR_ARRAY);
			glContext.glDisableClientState(GL10.GL_VERTEX_ARRAY);
		}
	}

	public void addPixel(Pixel pixel)
	{
		bornPixels.add(pixel);
	}

	public void removePixel(Pixel pixel)
	{
		if(!deadPixels.contains(pixel) && pixelMap.containsKey(pixel))
			deadPixels.add(pixel);
	}

	public void updatePixel(Pixel pixel)
	{
		Integer pixelIndex = pixelMap.get(pixel);
		if (pixelIndex == null)
			return;
		updateBuffers(pixelIndex, pixel);
	}

	public Set<Pixel> getPixels()
	{
		return pixelMap.keySet();
	}

	public String getPixelStats()
	{
		return livePixels + " out of a total of " + pixelCount + "possible. Maintaining " + deadPixels.size() + " dead pixels, while "+ bornPixels.size() + " are waiting to be spawned.";
	}

	private void updateBuffers(int offset, Pixel pixel)
	{
		synchronized(pixelLock)
		{
			vertexBuffer.put(offset * 2, Float.floatToIntBits( (int) pixel.getPosition().getXpos() + 0.5f ));
			vertexBuffer.put(offset * 2 + 1, Float.floatToIntBits( (int) pixel.getPosition().getYpos() + 0.5f ));
			colorBuffer.put(offset * 4, (byte) (pixel.getColor() >> 16));
			colorBuffer.put(offset * 4 + 1, (byte) (pixel.getColor() >> 8));
			colorBuffer.put(offset * 4 + 2, (byte) (pixel.getColor()));
			colorBuffer.put(offset * 4 + 3, (byte) (pixel.getColor() >> 24));
		}
	}

	private void swapPixels(Pixel newPixel, Pixel oldPixel)
	{
		Integer pixelIndex = pixelMap.remove(oldPixel);
		pixelMap.put(newPixel, pixelIndex);
		updateBuffers(pixelIndex, newPixel);
	}

	private void insertPixel(Pixel newPixel)
	{
		pixelMap.put(newPixel, livePixels);
		updateBuffers(livePixels++, newPixel);
	}
}
